package com.xdb.sqlparser.dialect.oracle.parser;

import java.util.ArrayList;
import java.util.List;

import com.xdb.sqlparser.ast.SQLDataTypeImpl;
import com.xdb.sqlparser.ast.SQLExpr;
import com.xdb.sqlparser.ast.SQLHint;
import com.xdb.sqlparser.ast.SQLName;
import com.xdb.sqlparser.ast.SQLStatement;
import com.xdb.sqlparser.ast.expr.SQLBinaryOpExpr;
import com.xdb.sqlparser.ast.expr.SQLBinaryOperator;
import com.xdb.sqlparser.ast.expr.SQLCharExpr;
import com.xdb.sqlparser.ast.expr.SQLNullExpr;
import com.xdb.sqlparser.ast.expr.SQLQueryExpr;
import com.xdb.sqlparser.ast.statement.SQLAlterTableAddColumn;
import com.xdb.sqlparser.ast.statement.SQLAlterTableDisableConstraint;
import com.xdb.sqlparser.ast.statement.SQLAlterTableDropColumnItem;
import com.xdb.sqlparser.ast.statement.SQLAlterTableDropConstraint;
import com.xdb.sqlparser.ast.statement.SQLAlterTableEnableConstraint;
import com.xdb.sqlparser.ast.statement.SQLAlterTableStatement;
import com.xdb.sqlparser.ast.statement.SQLColumnDefinition;
import com.xdb.sqlparser.ast.statement.SQLDropSequenceStatement;
import com.xdb.sqlparser.ast.statement.SQLDropTableStatement;
import com.xdb.sqlparser.ast.statement.SQLDropTriggerStatement;
import com.xdb.sqlparser.ast.statement.SQLDropUserStatement;
import com.xdb.sqlparser.ast.statement.SQLInsertInto;
import com.xdb.sqlparser.ast.statement.SQLRollbackStatement;
import com.xdb.sqlparser.ast.statement.SQLSelect;
import com.xdb.sqlparser.ast.statement.SQLSelectOrderByItem;
import com.xdb.sqlparser.ast.statement.SQLSelectStatement;
import com.xdb.sqlparser.ast.statement.SQLSetStatement;
import com.xdb.sqlparser.ast.statement.SQLTableSource;
import com.xdb.sqlparser.ast.statement.SQLUpdateSetItem;
import com.xdb.sqlparser.ast.statement.SQLUpdateStatement;
import com.xdb.sqlparser.dialect.oracle.ast.clause.OracleErrorLoggingClause;
import com.xdb.sqlparser.dialect.oracle.ast.clause.OracleParameter;
import com.xdb.sqlparser.dialect.oracle.ast.clause.OracleReturningClause;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterIndexStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterProcedureStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterSessionStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterSynonymStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableAddConstaint;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableDropPartition;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableModify;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableMoveTablespace;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableSplitPartition;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableTruncatePartition;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTablespaceAddDataFile;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTablespaceStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTriggerStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterViewStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleBlockStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleCommitStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleConstraint;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleCreateDatabaseDbLinkStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleCreateIndexStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleCreateProcedureStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleCreateSequenceStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleDeleteStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleDropDbLinkStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleExceptionStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleExitStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleExplainStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleExprStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleFetchStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleFileSpecification;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleForStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleGotoStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleIfStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleInsertStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleLabelStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleLockTableStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleLoopStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleMergeStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleMultiInsertStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OraclePLSQLCommitStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleSavePointStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleSetTransactionStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleStatement;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableSplitPartition.NestedTablePartitionSpec;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableSplitPartition.TableSpaceItem;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleAlterTableSplitPartition.UpdateIndexesClause;
import com.xdb.sqlparser.dialect.oracle.ast.statement.OracleLockTableStatement.LockMode;
import com.xdb.sqlparser.dialect.sql99.parser.SQL99StatementParser;
import com.xdb.sqlparser.exception.ParserException;
import com.xdb.sqlparser.parser.SQLLexer;
import com.xdb.sqlparser.parser.SQLToken;
import com.xdb.util.JDBCConstants;

public class OracleStatementParser extends SQL99StatementParser {

	public OracleStatementParser(String sql) {
		super(new OracleExprParser(sql));
	}

	public OracleStatementParser(SQLLexer lexer) {
		super(new OracleExprParser(lexer));
	}

	@Override
	public OracleExprParser getExprParser() {
		return (OracleExprParser) exprParser;
	}

	public void parseHints(List<SQLHint> hints) {
		this.getExprParser().parseHints(hints);
	}

	public OracleCreateTableParser getSQLCreateTableParser() {
		return new OracleCreateTableParser(lexer);
	}

	protected void parseInsert0_hinits(SQLInsertInto insertStatement) {
		if (insertStatement instanceof OracleInsertStatement) {
			OracleInsertStatement stmt = (OracleInsertStatement) insertStatement;
			this.getExprParser().parseHints(stmt.getHints());
		} else {
			List<SQLHint> hints = new ArrayList<SQLHint>(1);
			this.getExprParser().parseHints(hints);
		}
	}

	public void parseStatementList(List<SQLStatement> statementList, int max) {
		for (;;) {
			if (max != -1) {
				if (statementList.size() >= max) {
					return;
				}
			}

			if (lexer.token() == SQLToken.EOF) {
				return;
			}
			if (lexer.token() == SQLToken.END) {
				return;
			}
			if (lexer.token() == SQLToken.ELSE) {
				return;
			}

			if (lexer.token() == (SQLToken.SEMI)) {
				lexer.nextToken();
				continue;
			}

			if (lexer.token() == (SQLToken.SELECT)) {
				SQLSelectStatement stmt = new SQLSelectStatement(new OracleSelectParser(this.exprParser).select(), JDBCConstants.ORACLE);
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == (SQLToken.UPDATE)) {
				statementList.add(parseUpdateStatement());
				continue;
			}

			if (lexer.token() == (SQLToken.CREATE)) {
				statementList.add(parseCreate());
				continue;
			}

			if (lexer.token() == SQLToken.INSERT) {
				statementList.add(parseInsert());
				continue;
			}

			if (lexer.token() == (SQLToken.DELETE)) {
				statementList.add(parseDeleteStatement());
				continue;
			}

			if (lexer.token() == (SQLToken.SLASH)) {
				lexer.nextToken();
				statementList.add(new OraclePLSQLCommitStatement());
				continue;
			}

			if (lexer.token() == SQLToken.ALTER) {
				statementList.add(parserAlter());
				continue;
			}

			if (lexer.token() == SQLToken.WITH) {
				statementList.add(new SQLSelectStatement(new OracleSelectParser(this.exprParser).select()));
				continue;
			}

			if (lexer.token() == SQLToken.LBRACE || identifierEquals("CALL")) {
				statementList.add(this.parseCall());
				continue;
			}

			if (lexer.token() == SQLToken.MERGE) {
				statementList.add(this.parseMerge());
				continue;
			}

			if (lexer.token() == SQLToken.BEGIN) {
				statementList.add(this.parseBlock());
				continue;
			}

			if (lexer.token() == SQLToken.DECLARE) {
				statementList.add(this.parseBlock());
				continue;
			}

			if (lexer.token() == SQLToken.LOCK) {
				statementList.add(this.parseLock());
				continue;
			}

			if (lexer.token() == SQLToken.TRUNCATE) {
				statementList.add(this.parseTruncate());
				continue;
			}

			if (lexer.token() == SQLToken.VARIANT) {
				SQLExpr variant = this.exprParser.primary();
				if (variant instanceof SQLBinaryOpExpr) {
					SQLBinaryOpExpr binaryOpExpr = (SQLBinaryOpExpr) variant;
					if (binaryOpExpr.getOperator() == SQLBinaryOperator.Assignment) {
						SQLSetStatement stmt = new SQLSetStatement(binaryOpExpr.getLeft(), binaryOpExpr.getRight(), getDbType());
						statementList.add(stmt);
						continue;
					}
				}
				accept(SQLToken.COLONEQ);
				SQLExpr value = this.exprParser.expr();

				SQLSetStatement stmt = new SQLSetStatement(variant, value, getDbType());
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.EXCEPTION) {
				statementList.add(this.parseException());
				continue;
			}

			if (identifierEquals("EXIT")) {
				lexer.nextToken();
				OracleExitStatement stmt = new OracleExitStatement();
				if (lexer.token() == SQLToken.WHEN) {
					lexer.nextToken();
					stmt.setWhen(this.exprParser.expr());
				}
				statementList.add(stmt);
				continue;
			}

			if (identifierEquals("FETCH")) {
				lexer.nextToken();
				OracleFetchStatement stmt = new OracleFetchStatement();
				stmt.setCursorName(this.exprParser.name());
				accept(SQLToken.INTO);
				for (;;) {
					stmt.getInto().add(this.exprParser.name());
					if (lexer.token() == SQLToken.COMMA) {
						lexer.nextToken();
						continue;
					}

					break;
				}
				statementList.add(stmt);
				continue;
			}

			if (identifierEquals("ROLLBACK")) {
				SQLRollbackStatement stmt = parseRollback();

				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.EXPLAIN) {
				statementList.add(this.parseExplain());
				continue;
			}

			if (lexer.token() == SQLToken.IDENTIFIER) {
				SQLExpr expr = exprParser.expr();
				OracleExprStatement stmt = new OracleExprStatement(expr);
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.LPAREN) {
				char ch = lexer.current();
				int bp = lexer.bp();
				lexer.nextToken();

				if (lexer.token() == SQLToken.SELECT) {
					lexer.reset(bp, ch, SQLToken.LPAREN);
					statementList.add(this.parseSelect());
					continue;
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
			}

			if (lexer.token() == SQLToken.SET) {
				statementList.add(this.parseSet());
				continue;
			}

			if (lexer.token() == SQLToken.GRANT) {
				statementList.add(this.parseGrant());
				continue;
			}

			if (lexer.token() == SQLToken.REVOKE) {
				statementList.add(this.parseRevoke());
				continue;
			}

			if (lexer.token() == SQLToken.COMMENT) {
				statementList.add(this.parseComment());
				continue;
			}
			if (lexer.token() == SQLToken.FOR) {
				statementList.add(this.parseFor());
				continue;
			}
			if (lexer.token() == SQLToken.LOOP) {
				statementList.add(this.parseLoop());
				continue;
			}
			if (lexer.token() == SQLToken.IF) {
				statementList.add(this.parseIf());
				continue;
			}

			if (lexer.token() == SQLToken.GOTO) {
				lexer.nextToken();
				SQLName label = this.exprParser.name();
				OracleGotoStatement stmt = new OracleGotoStatement(label);
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.COMMIT) {
				lexer.nextToken();

				if (identifierEquals("WORK")) {
					lexer.nextToken();
				}
				OracleCommitStatement stmt = new OracleCommitStatement();

				if (identifierEquals("WRITE")) {
					stmt.setWrite(true);
					lexer.nextToken();

					for (;;) {
						if (lexer.token() == SQLToken.WAIT) {
							lexer.nextToken();
							stmt.setWait(Boolean.TRUE);
							continue;
						} else if (lexer.token() == SQLToken.NOWAIT) {
							lexer.nextToken();
							stmt.setWait(Boolean.FALSE);
							continue;
						} else if (lexer.token() == SQLToken.IMMEDIATE) {
							lexer.nextToken();
							stmt.setImmediate(Boolean.TRUE);
							continue;
						} else if (identifierEquals("BATCH")) {
							lexer.nextToken();
							stmt.setImmediate(Boolean.FALSE);
							continue;
						}

						break;
					}
				}

				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.SAVEPOINT) {
				lexer.nextToken();

				OracleSavePointStatement stmt = new OracleSavePointStatement();

				if (lexer.token() == SQLToken.TO) {
					lexer.nextToken();
					stmt.setTo(this.exprParser.name());
				}

				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.LTLT) {
				lexer.nextToken();
				SQLName label = this.exprParser.name();
				OracleLabelStatement stmt = new OracleLabelStatement(label);
				accept(SQLToken.GTGT);
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.DROP) {
				lexer.nextToken();

				if (lexer.token() == SQLToken.TABLE) {
					SQLDropTableStatement stmt = parseDropTable(false);
					statementList.add(stmt);
					continue;
				}

				boolean isPublic = false;
				if (identifierEquals("PUBLIC")) {
					lexer.nextToken();
					isPublic = true;
				}

				if (lexer.token() == SQLToken.DATABASE) {
					lexer.nextToken();

					if (identifierEquals("LINK")) {
						lexer.nextToken();

						OracleDropDbLinkStatement stmt = new OracleDropDbLinkStatement();
						if (isPublic) {
							stmt.setPublic(isPublic);
						}

						stmt.setName(this.exprParser.name());

						statementList.add(stmt);
						continue;
					}
				}

				if (lexer.token() == SQLToken.INDEX) {
					SQLStatement stmt = parseDropIndex();
					statementList.add(stmt);
					continue;
				}

				if (lexer.token() == SQLToken.VIEW) {
					SQLStatement stmt = parseDropView(false);
					statementList.add(stmt);
					continue;
				}

				if (lexer.token() == SQLToken.SEQUENCE) {
					SQLDropSequenceStatement stmt = parseDropSequece(false);
					statementList.add(stmt);
					continue;
				}

				if (lexer.token() == SQLToken.TRIGGER) {
					SQLDropTriggerStatement stmt = parseDropTrigger(false);
					statementList.add(stmt);
					continue;
				}

				if (lexer.token() == SQLToken.USER) {
					SQLDropUserStatement stmt = parseDropUser();
					statementList.add(stmt);
					continue;
				}

				throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
			}

			if (lexer.token() == SQLToken.NULL) {
				lexer.nextToken();
				OracleExprStatement stmt = new OracleExprStatement(new SQLNullExpr());
				statementList.add(stmt);
				continue;
			}

			throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
		}
	}

	public SQLStatement parseIf() {
		accept(SQLToken.IF);

		OracleIfStatement stmt = new OracleIfStatement();

		stmt.setCondition(this.exprParser.expr());

		accept(SQLToken.THEN);

		this.parseStatementList(stmt.getStatements());

		while (lexer.token() == SQLToken.ELSE) {
			lexer.nextToken();

			if (lexer.token() == SQLToken.IF) {
				lexer.nextToken();

				OracleIfStatement.ElseIf elseIf = new OracleIfStatement.ElseIf();

				elseIf.setCondition(this.exprParser.expr());

				accept(SQLToken.THEN);
				this.parseStatementList(elseIf.getStatements());

				stmt.getElseIfList().add(elseIf);
			} else {
				OracleIfStatement.Else elseItem = new OracleIfStatement.Else();
				this.parseStatementList(elseItem.getStatements());
				stmt.setElseItem(elseItem);
				break;
			}
		}

		accept(SQLToken.END);
		accept(SQLToken.IF);

		return stmt;
	}

	public OracleForStatement parseFor() {
		accept(SQLToken.FOR);

		OracleForStatement stmt = new OracleForStatement();

		stmt.setIndex(this.exprParser.name());
		accept(SQLToken.IN);
		stmt.setRange(this.exprParser.expr());
		accept(SQLToken.LOOP);

		this.parseStatementList(stmt.getStatements());
		accept(SQLToken.END);
		accept(SQLToken.LOOP);
		return stmt;
	}

	public OracleLoopStatement parseLoop() {
		accept(SQLToken.LOOP);

		OracleLoopStatement stmt = new OracleLoopStatement();

		this.parseStatementList(stmt.getStatements());
		accept(SQLToken.END);
		accept(SQLToken.LOOP);
		return stmt;
	}

	public SQLStatement parseSet() {
		accept(SQLToken.SET);
		acceptIdentifier("TRANSACTION");

		OracleSetTransactionStatement stmt = new OracleSetTransactionStatement();

		if (identifierEquals("READ")) {
			lexer.nextToken();
			acceptIdentifier("ONLY");
			stmt.setReadOnly(true);
		}

		acceptIdentifier("NAME");

		stmt.setName(this.exprParser.expr());
		return stmt;
	}

	public OracleStatement parserAlter() {
		accept(SQLToken.ALTER);
		if (lexer.token() == SQLToken.SESSION) {
			lexer.nextToken();

			OracleAlterSessionStatement stmt = new OracleAlterSessionStatement();
			if (lexer.token() == SQLToken.SET) {
				lexer.nextToken();
				parseAssignItems(stmt.getItems(), stmt);
			} else {
				throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
			}
			return stmt;
		} else if (lexer.token() == SQLToken.PROCEDURE) {
			lexer.nextToken();
			OracleAlterProcedureStatement stmt = new OracleAlterProcedureStatement();
			stmt.setName(this.exprParser.name());
			if (identifierEquals("COMPILE")) {
				lexer.nextToken();
				stmt.setCompile(true);
			}

			if (identifierEquals("REUSE")) {
				lexer.nextToken();
				acceptIdentifier("SETTINGS");
				stmt.setReuseSettings(true);
			}

			return stmt;
		} else if (lexer.token() == SQLToken.TABLE) {
			return parseAlterTable();
		} else if (lexer.token() == SQLToken.INDEX) {
			lexer.nextToken();
			OracleAlterIndexStatement stmt = new OracleAlterIndexStatement();
			stmt.setName(this.exprParser.name());

			if (identifierEquals("RENAME")) {
				lexer.nextToken();
				accept(SQLToken.TO);
				stmt.setRenameTo(this.exprParser.name());
			}

			for (;;) {
				if (identifierEquals("rebuild")) {
					lexer.nextToken();

					OracleAlterIndexStatement.Rebuild rebuild = new OracleAlterIndexStatement.Rebuild();
					stmt.setRebuild(rebuild);
					continue;
				} else if (identifierEquals("MONITORING")) {
					lexer.nextToken();
					acceptIdentifier("USAGE");
					stmt.setMonitoringUsage(Boolean.TRUE);
					continue;
				} else if (identifierEquals("PARALLEL")) {
					lexer.nextToken();
					stmt.setParallel(this.exprParser.expr());
				}
				break;
			}

			return stmt;
		} else if (lexer.token() == SQLToken.TRIGGER) {
			lexer.nextToken();
			OracleAlterTriggerStatement stmt = new OracleAlterTriggerStatement();
			stmt.setName(this.exprParser.name());

			for (;;) {
				if (lexer.token() == SQLToken.ENABLE) {
					lexer.nextToken();
					stmt.setEnable(Boolean.TRUE);
					continue;
				} else if (lexer.token() == SQLToken.DISABLE) {
					lexer.nextToken();
					stmt.setEnable(Boolean.FALSE);
					continue;
				} else if (identifierEquals("COMPILE")) {
					lexer.nextToken();
					stmt.setCompile(true);
					continue;
				}
				break;
			}

			return stmt;
		} else if (identifierEquals("SYNONYM")) {
			lexer.nextToken();
			OracleAlterSynonymStatement stmt = new OracleAlterSynonymStatement();
			stmt.setName(this.exprParser.name());

			for (;;) {
				if (lexer.token() == SQLToken.ENABLE) {
					lexer.nextToken();
					stmt.setEnable(Boolean.TRUE);
					continue;
				} else if (lexer.token() == SQLToken.DISABLE) {
					lexer.nextToken();
					stmt.setEnable(Boolean.FALSE);
					continue;
				} else if (identifierEquals("COMPILE")) {
					lexer.nextToken();
					stmt.setCompile(true);
					continue;
				}
				break;
			}

			return stmt;
		} else if (lexer.token() == SQLToken.VIEW) {
			lexer.nextToken();
			OracleAlterViewStatement stmt = new OracleAlterViewStatement();
			stmt.setName(this.exprParser.name());

			for (;;) {
				if (lexer.token() == SQLToken.ENABLE) {
					lexer.nextToken();
					stmt.setEnable(Boolean.TRUE);
					continue;
				} else if (lexer.token() == SQLToken.DISABLE) {
					lexer.nextToken();
					stmt.setEnable(Boolean.FALSE);
					continue;
				} else if (identifierEquals("COMPILE")) {
					lexer.nextToken();
					stmt.setCompile(true);
					continue;
				}
				break;
			}

			return stmt;
		} else if (lexer.token() == SQLToken.TABLESPACE) {
			lexer.nextToken();

			OracleAlterTablespaceStatement stmt = new OracleAlterTablespaceStatement();
			stmt.setName(this.exprParser.name());

			if (identifierEquals("ADD")) {
				lexer.nextToken();

				if (identifierEquals("DATAFILE")) {
					lexer.nextToken();

					OracleAlterTablespaceAddDataFile item = new OracleAlterTablespaceAddDataFile();

					for (;;) {
						OracleFileSpecification file = new OracleFileSpecification();

						for (;;) {
							SQLExpr fileName = this.exprParser.expr();
							file.getFileNames().add(fileName);

							if (lexer.token() == SQLToken.COMMA) {
								lexer.nextToken();
								continue;
							}

							break;
						}

						if (identifierEquals("SIZE")) {
							lexer.nextToken();
							file.setSize(this.exprParser.expr());
						}

						if (identifierEquals("AUTOEXTEND")) {
							lexer.nextToken();
							if (identifierEquals("OFF")) {
								lexer.nextToken();
								file.setAutoExtendOff(true);
							} else if (identifierEquals("ON")) {
								lexer.nextToken();
								file.setAutoExtendOn(this.exprParser.expr());
							} else {
								throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
							}
						}

						item.getFiles().add(file);

						if (lexer.token() == SQLToken.COMMA) {
							lexer.nextToken();
							continue;
						}

						break;
					}

					stmt.setItem(item);
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
			} else {
				throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
			}

			return stmt;
		}

		throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
	}

	private OracleStatement parseAlterTable() {
		lexer.nextToken();
		OracleAlterTableStatement stmt = new OracleAlterTableStatement();
		stmt.setName(this.exprParser.name());

		for (;;) {
			if (identifierEquals("ADD")) {
				lexer.nextToken();

				if (lexer.token() == SQLToken.LPAREN) {
					lexer.nextToken();

					SQLAlterTableAddColumn item = parseAlterTableAddColumn();

					stmt.getItems().add(item);

					accept(SQLToken.RPAREN);
				} else if (lexer.token() == SQLToken.CONSTRAINT) {
					OracleConstraint constraint = ((OracleExprParser) this.exprParser).parseConstaint();
					OracleAlterTableAddConstaint item = new OracleAlterTableAddConstaint();
					constraint.setParent(item);
					item.setParent(stmt);
					item.setConstraint(constraint);
					stmt.getItems().add(item);
				} else if (lexer.token() == SQLToken.IDENTIFIER) {
					SQLAlterTableAddColumn item = parseAlterTableAddColumn();
					stmt.getItems().add(item);
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}

				continue;
			} else if (identifierEquals("MOVE")) {
				lexer.nextToken();

				if (lexer.token() == SQLToken.TABLESPACE) {
					lexer.nextToken();

					OracleAlterTableMoveTablespace item = new OracleAlterTableMoveTablespace();
					item.setName(this.exprParser.name());

					stmt.getItems().add(item);
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
			} else if (identifierEquals("RENAME")) {
				stmt.getItems().add(parseAlterTableRename());
			} else if (identifierEquals("MODIFY")) {
				lexer.nextToken();

				OracleAlterTableModify item = new OracleAlterTableModify();
				if (lexer.token() == SQLToken.LPAREN) {
					lexer.nextToken();

					for (;;) {
						SQLColumnDefinition columnDef = this.exprParser.parseColumn();
						item.getColumns().add(columnDef);
						if (lexer.token() == SQLToken.COMMA) {
							lexer.nextToken();
							continue;
						}
						break;
					}
					accept(SQLToken.RPAREN);

				} else {
					SQLColumnDefinition columnDef = this.exprParser.parseColumn();
					item.getColumns().add(columnDef);
				}

				stmt.getItems().add(item);
				continue;
			} else if (identifierEquals("SPLIT")) {
				parseAlterTableSplit(stmt);
				continue;
			} else if (lexer.token() == SQLToken.TRUNCATE) {
				lexer.nextToken();
				if (identifierEquals("PARTITION")) {
					lexer.nextToken();
					OracleAlterTableTruncatePartition item = new OracleAlterTableTruncatePartition();
					item.setName(this.exprParser.name());
					stmt.getItems().add(item);
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
				continue;
			} else if (lexer.token() == SQLToken.DROP) {
				parseAlterDrop(stmt);
				continue;
			} else if (lexer.token() == SQLToken.DISABLE) {
				lexer.nextToken();
				if (lexer.token() == SQLToken.CONSTRAINT) {
					lexer.nextToken();
					SQLAlterTableEnableConstraint item = new SQLAlterTableEnableConstraint();
					item.setConstraintName(this.exprParser.name());
					stmt.getItems().add(item);
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
			} else if (lexer.token() == SQLToken.ENABLE) {
				lexer.nextToken();
				if (lexer.token() == SQLToken.CONSTRAINT) {
					lexer.nextToken();
					SQLAlterTableDisableConstraint item = new SQLAlterTableDisableConstraint();
					item.setConstraintName(this.exprParser.name());
					stmt.getItems().add(item);
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
			}

			break;
		}

		if (lexer.token() == SQLToken.UPDATE) {
			lexer.nextToken();

			if (identifierEquals("GLOBAL")) {
				lexer.nextToken();
				acceptIdentifier("INDEXES");
				stmt.setUpdateGlobalIndexes(true);
			} else {
				throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
			}
		}

		return stmt;
	}

	public void parseAlterDrop(SQLAlterTableStatement stmt) {
		lexer.nextToken();
		if (lexer.token() == SQLToken.CONSTRAINT) {
			lexer.nextToken();
			SQLAlterTableDropConstraint item = new SQLAlterTableDropConstraint();
			item.setConstraintName(this.exprParser.name());
			stmt.getItems().add(item);
		} else if (lexer.token() == SQLToken.LPAREN) {
			lexer.nextToken();
			SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
			this.exprParser.names(item.getColumns());
			stmt.getItems().add(item);
			accept(SQLToken.RPAREN);
		} else if (lexer.token() == SQLToken.COLUMN) {
			lexer.nextToken();
			SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
			this.exprParser.names(item.getColumns());
			stmt.getItems().add(item);
		} else if (identifierEquals("PARTITION")) {
			lexer.nextToken();
			OracleAlterTableDropPartition item = new OracleAlterTableDropPartition();
			item.setName(this.exprParser.name());
			stmt.getItems().add(item);
		} else {
			throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
		}
	}

	private void parseAlterTableSplit(OracleAlterTableStatement stmt) {
		lexer.nextToken();
		if (identifierEquals("PARTITION")) {
			lexer.nextToken();
			OracleAlterTableSplitPartition item = new OracleAlterTableSplitPartition();
			item.setName(this.exprParser.name());

			if (identifierEquals("AT")) {
				lexer.nextToken();
				accept(SQLToken.LPAREN);
				this.exprParser.exprList(item.getAt(), item);
				accept(SQLToken.RPAREN);
			} else {
				throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
			}

			if (lexer.token() == SQLToken.INTO) {
				lexer.nextToken();
				accept(SQLToken.LPAREN);

				for (;;) {
					NestedTablePartitionSpec spec = new NestedTablePartitionSpec();
					acceptIdentifier("PARTITION");
					spec.setPartition(this.exprParser.name());

					for (;;) {
						if (lexer.token() == SQLToken.TABLESPACE) {
							lexer.nextToken();
							SQLName tablespace = this.exprParser.name();
							spec.getSegmentAttributeItems().add(new TableSpaceItem(tablespace));
							continue;
						} else if (identifierEquals("PCTREE")) {
							throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
						} else if (identifierEquals("PCTUSED")) {
							throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
						} else if (identifierEquals("INITRANS")) {
							throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());

						} else if (identifierEquals("STORAGE")) {
							throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());

						} else if (identifierEquals("LOGGING")) {
							throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
						} else if (identifierEquals("NOLOGGING")) {
							throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
						} else if (identifierEquals("FILESYSTEM_LIKE_LOGGING")) {
							throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());

						}

						break;
					}

					item.getInto().add(spec);

					if (lexer.token() == SQLToken.COMMA) {
						lexer.nextToken();
						continue;
					}
					break;
				}
				accept(SQLToken.RPAREN);
			}

			if (lexer.token() == SQLToken.UPDATE) {
				lexer.nextToken();
				acceptIdentifier("INDEXES");
				UpdateIndexesClause updateIndexes = new UpdateIndexesClause();
				item.setUpdateIndexes(updateIndexes);
			}
			stmt.getItems().add(item);
		} else {
			throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
		}
	}

	public OracleLockTableStatement parseLock() {
		accept(SQLToken.LOCK);
		accept(SQLToken.TABLE);

		OracleLockTableStatement stmt = new OracleLockTableStatement();
		stmt.setTable(this.exprParser.name());

		accept(SQLToken.IN);
		if (lexer.token() == SQLToken.SHARE) {
			stmt.setLockMode(LockMode.SHARE);
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.EXCLUSIVE) {
			stmt.setLockMode(LockMode.EXCLUSIVE);
			lexer.nextToken();
		}
		accept(SQLToken.MODE);

		if (lexer.token() == SQLToken.NOWAIT) {
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.WAIT) {
			lexer.nextToken();
			stmt.setWait(exprParser.expr());
		}
		return stmt;
	}

	public OracleBlockStatement parseBlock() {
		OracleBlockStatement block = new OracleBlockStatement();

		if (lexer.token() == SQLToken.DECLARE) {
			lexer.nextToken();

			parserParameters(block.getParameters());
			for (OracleParameter param : block.getParameters()) {
				param.setParent(block);
			}
		}

		accept(SQLToken.BEGIN);

		parseStatementList(block.getStatementList());

		accept(SQLToken.END);

		return block;
	}

	private void parserParameters(List<OracleParameter> parameters) {
		for (;;) {
			OracleParameter parameter = new OracleParameter();

			if (lexer.token() == SQLToken.CURSOR) {
				lexer.nextToken();

				parameter.setName(this.exprParser.name());

				accept(SQLToken.IS);
				SQLSelect select = this.createSQLSelectParser().select();

				SQLDataTypeImpl dataType = new SQLDataTypeImpl();
				dataType.setName("CURSOR");
				parameter.setDataType(dataType);

				parameter.setDefaultValue(new SQLQueryExpr(select));
			} else {
				parameter.setName(this.exprParser.name());
				parameter.setDataType(this.exprParser.parseDataType());

				if (lexer.token() == SQLToken.COLONEQ) {
					lexer.nextToken();
					parameter.setDefaultValue(this.exprParser.expr());
				}
			}

			parameters.add(parameter);
			if (lexer.token() == SQLToken.COMMA || lexer.token() == SQLToken.SEMI) {
				lexer.nextToken();
			}

			if (lexer.token() != SQLToken.BEGIN && lexer.token() != SQLToken.RPAREN) {
				continue;
			}

			break;
		}
	}

	public OracleSelectParser createSQLSelectParser() {
		return new OracleSelectParser(this.exprParser);
	}

	public OracleMergeStatement parseMerge() {
		accept(SQLToken.MERGE);

		OracleMergeStatement stmt = new OracleMergeStatement();

		parseHints(stmt.getHints());

		accept(SQLToken.INTO);
		stmt.setInto(exprParser.name());

		stmt.setAlias(as());

		accept(SQLToken.USING);

		SQLTableSource using = this.createSQLSelectParser().parseTableSource();
		stmt.setUsing(using);

		accept(SQLToken.ON);
		stmt.setOn(exprParser.expr());

		boolean insertFlag = false;
		if (lexer.token() == SQLToken.WHEN) {
			lexer.nextToken();
			if (lexer.token() == SQLToken.MATCHED) {
				OracleMergeStatement.MergeUpdateClause updateClause = new OracleMergeStatement.MergeUpdateClause();
				lexer.nextToken();
				accept(SQLToken.THEN);
				accept(SQLToken.UPDATE);
				accept(SQLToken.SET);

				for (;;) {
					SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();

					updateClause.getItems().add(item);
					item.setParent(updateClause);

					if (lexer.token() == (SQLToken.COMMA)) {
						lexer.nextToken();
						continue;
					}

					break;
				}

				if (lexer.token() == SQLToken.WHERE) {
					lexer.nextToken();
					updateClause.setWhere(exprParser.expr());
				}

				if (lexer.token() == SQLToken.DELETE) {
					lexer.nextToken();
					accept(SQLToken.WHERE);
					updateClause.setWhere(exprParser.expr());
				}

				stmt.setUpdateClause(updateClause);
			} else if (lexer.token() == SQLToken.NOT) {
				lexer.nextToken();
				insertFlag = true;
			}
		}

		if (!insertFlag) {
			if (lexer.token() == SQLToken.WHEN) {
				lexer.nextToken();
			}

			if (lexer.token() == SQLToken.NOT) {
				lexer.nextToken();
				insertFlag = true;
			}
		}

		if (insertFlag) {
			OracleMergeStatement.MergeInsertClause insertClause = new OracleMergeStatement.MergeInsertClause();

			accept(SQLToken.MATCHED);
			accept(SQLToken.THEN);
			accept(SQLToken.INSERT);

			if (lexer.token() == SQLToken.LPAREN) {
				accept(SQLToken.LPAREN);
				exprParser.exprList(insertClause.getColumns(), insertClause);
				accept(SQLToken.RPAREN);
			}
			accept(SQLToken.VALUES);
			accept(SQLToken.LPAREN);
			exprParser.exprList(insertClause.getValues(), insertClause);
			accept(SQLToken.RPAREN);

			if (lexer.token() == SQLToken.WHERE) {
				lexer.nextToken();
				insertClause.setWhere(exprParser.expr());
			}

			stmt.setInsertClause(insertClause);
		}

		OracleErrorLoggingClause errorClause = parseErrorLoggingClause();
		stmt.setErrorLoggingClause(errorClause);

		return stmt;
	}

	public OracleStatement parseInsert() {
		if (lexer.token() == SQLToken.LPAREN) {
			OracleInsertStatement stmt = new OracleInsertStatement();
			parseInsert0(stmt, false);

			stmt.setReturning(parseReturningClause());
			stmt.setErrorLogging(parseErrorLoggingClause());

			return stmt;
		}

		accept(SQLToken.INSERT);

		List<SQLHint> hints = new ArrayList<SQLHint>();

		parseHints(hints);

		if (lexer.token() == SQLToken.INTO) {
			OracleInsertStatement stmt = new OracleInsertStatement();
			stmt.setHints(hints);

			parseInsert0(stmt);

			stmt.setReturning(parseReturningClause());
			stmt.setErrorLogging(parseErrorLoggingClause());

			return stmt;
		}

		OracleMultiInsertStatement stmt = parseMultiInsert();
		stmt.setHints(hints);
		return stmt;
	}

	public OracleMultiInsertStatement parseMultiInsert() {
		OracleMultiInsertStatement stmt = new OracleMultiInsertStatement();

		if (lexer.token() == SQLToken.ALL) {
			lexer.nextToken();
			stmt.setOption(OracleMultiInsertStatement.Option.ALL);
		} else if (lexer.token() == SQLToken.FIRST) {
			lexer.nextToken();
			stmt.setOption(OracleMultiInsertStatement.Option.FIRST);
		}

		while (lexer.token() == SQLToken.INTO) {
			OracleMultiInsertStatement.InsertIntoClause clause = new OracleMultiInsertStatement.InsertIntoClause();

			parseInsert0(clause);

			clause.setReturning(parseReturningClause());
			clause.setErrorLogging(parseErrorLoggingClause());

			stmt.getEntries().add(clause);
		}

		if (lexer.token() == SQLToken.WHEN) {
			OracleMultiInsertStatement.ConditionalInsertClause clause = new OracleMultiInsertStatement.ConditionalInsertClause();

			while (lexer.token() == SQLToken.WHEN) {
				lexer.nextToken();

				OracleMultiInsertStatement.ConditionalInsertClauseItem item = new OracleMultiInsertStatement.ConditionalInsertClauseItem();

				item.setWhen(this.exprParser.expr());
				accept(SQLToken.THEN);
				OracleMultiInsertStatement.InsertIntoClause insertInto = new OracleMultiInsertStatement.InsertIntoClause();
				parseInsert0(insertInto);
				item.setThen(insertInto);

				clause.getItems().add(item);
			}

			if (lexer.token() == SQLToken.ELSE) {
				lexer.nextToken();

				OracleMultiInsertStatement.InsertIntoClause insertInto = new OracleMultiInsertStatement.InsertIntoClause();
				parseInsert0(insertInto, false);
				clause.setElseItem(insertInto);
			}
			stmt.getEntries().add(clause);
		}

		SQLSelect subQuery = this.createSQLSelectParser().select();
		stmt.setSubQuery(subQuery);

		return stmt;
	}

	private OracleExceptionStatement parseException() {
		accept(SQLToken.EXCEPTION);
		OracleExceptionStatement stmt = new OracleExceptionStatement();

		for (;;) {
			accept(SQLToken.WHEN);
			OracleExceptionStatement.Item item = new OracleExceptionStatement.Item();
			item.setWhen(this.exprParser.expr());
			accept(SQLToken.THEN);
			parseStatementList(item.getStatements());

			stmt.getItems().add(item);

			if (lexer.token() != SQLToken.WHEN) {
				break;
			}
		}
		return stmt;
	}

	private OracleErrorLoggingClause parseErrorLoggingClause() {
		if (identifierEquals("LOG")) {
			OracleErrorLoggingClause errorClause = new OracleErrorLoggingClause();

			lexer.nextToken();
			accept(SQLToken.ERRORS);
			if (lexer.token() == SQLToken.INTO) {
				lexer.nextToken();
				errorClause.setInto(exprParser.name());
			}

			if (lexer.token() == SQLToken.LPAREN) {
				lexer.nextToken();
				errorClause.setSimpleExpression(exprParser.expr());
				accept(SQLToken.RPAREN);
			}

			if (lexer.token() == SQLToken.REJECT) {
				lexer.nextToken();
				accept(SQLToken.LIMIT);
				errorClause.setLimit(exprParser.expr());
			}

			return errorClause;
		}
		return null;
	}

	public OracleReturningClause parseReturningClause() {
		OracleReturningClause clause = null;

		if (lexer.token() == SQLToken.RETURNING) {
			lexer.nextToken();
			clause = new OracleReturningClause();

			for (;;) {
				SQLExpr item = exprParser.expr();
				clause.getItems().add(item);
				if (lexer.token() == SQLToken.COMMA) {
					lexer.nextToken();
					continue;
				}
				break;
			}
			accept(SQLToken.INTO);
			for (;;) {
				SQLExpr item = exprParser.expr();
				clause.getValues().add(item);
				if (lexer.token() == SQLToken.COMMA) {
					lexer.nextToken();
					continue;
				}
				break;
			}
		}
		return clause;
	}

	public OracleExplainStatement parseExplain() {
		accept(SQLToken.EXPLAIN);
		acceptIdentifier("PLAN");
		OracleExplainStatement stmt = new OracleExplainStatement();

		if (lexer.token() == SQLToken.SET) {
			lexer.nextToken();
			acceptIdentifier("STATEMENT_ID");
			accept(SQLToken.EQ);
			stmt.setStatementId((SQLCharExpr) this.exprParser.primary());
		}

		if (lexer.token() == SQLToken.INTO) {
			lexer.nextToken();
			stmt.setInto(this.exprParser.name());
		}

		accept(SQLToken.FOR);
		stmt.setStatement(parseStatement());

		return stmt;
	}

	public OracleDeleteStatement parseDeleteStatement() {
		OracleDeleteStatement deleteStatement = new OracleDeleteStatement();

		if (lexer.token() == SQLToken.DELETE) {
			lexer.nextToken();

			if (lexer.token() == SQLToken.COMMENT) {
				lexer.nextToken();
			}

			parseHints(deleteStatement.getHints());

			if (lexer.token() == (SQLToken.FROM)) {
				lexer.nextToken();
			}

			if (identifierEquals("ONLY")) {
				lexer.nextToken();
				accept(SQLToken.LPAREN);

				SQLName tableName = exprParser.name();
				deleteStatement.setTableName(tableName);

				accept(SQLToken.RPAREN);
			} else {
				SQLName tableName = exprParser.name();
				deleteStatement.setTableName(tableName);
			}

			deleteStatement.setAlias(as());
		}

		if (lexer.token() == (SQLToken.WHERE)) {
			lexer.nextToken();
			deleteStatement.setWhere(this.exprParser.expr());
		}

		if (lexer.token() == SQLToken.RETURNING) {
			OracleReturningClause clause = this.parseReturningClause();
			deleteStatement.setReturning(clause);
		}
		if (identifierEquals("RETURN") || identifierEquals("RETURNING")) {
			throw new ParserException("TODO");
		}

		if (identifierEquals("LOG")) {
			throw new ParserException("TODO");
		}

		return deleteStatement;
	}

	public SQLStatement parseCreateDbLink() {
		accept(SQLToken.CREATE);

		OracleCreateDatabaseDbLinkStatement dbLink = new OracleCreateDatabaseDbLinkStatement();

		if (identifierEquals("SHARED")) {
			dbLink.setShared(true);
			lexer.nextToken();
		}

		if (identifierEquals("PUBLIC")) {
			dbLink.setPublic(true);
			lexer.nextToken();
		}

		accept(SQLToken.DATABASE);
		acceptIdentifier("LINK");

		dbLink.setName(this.exprParser.name());

		if (lexer.token() == SQLToken.CONNECT) {
			lexer.nextToken();
			accept(SQLToken.TO);

			dbLink.setUser(this.exprParser.name());

			if (lexer.token() == SQLToken.IDENTIFIED) {
				lexer.nextToken();
				accept(SQLToken.BY);
				dbLink.setPassword(lexer.stringVal());
				accept(SQLToken.IDENTIFIER);
			}
		}

		if (identifierEquals("AUTHENTICATED")) {
			lexer.nextToken();
			accept(SQLToken.BY);
			dbLink.setAuthenticatedUser(this.exprParser.name());

			accept(SQLToken.IDENTIFIED);
			accept(SQLToken.BY);
			dbLink.setPassword(lexer.stringVal());
			accept(SQLToken.IDENTIFIER);
		}

		if (lexer.token() == SQLToken.USING) {
			lexer.nextToken();
			dbLink.setUsing(this.exprParser.expr());
		}

		return dbLink;
	}

	public OracleCreateIndexStatement parseCreateIndex(boolean acceptCreate) {
		if (acceptCreate) {
			accept(SQLToken.CREATE);
		}

		OracleCreateIndexStatement stmt = new OracleCreateIndexStatement();
		if (lexer.token() == SQLToken.UNIQUE) {
			stmt.setType("UNIQUE");
			lexer.nextToken();
		} else if (identifierEquals("BITMAP")) {
			stmt.setType("BITMAP");
			lexer.nextToken();
		}

		accept(SQLToken.INDEX);

		stmt.setName(this.exprParser.name());

		accept(SQLToken.ON);

		stmt.setTable(this.exprParser.name());

		accept(SQLToken.LPAREN);

		for (;;) {
			SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
			stmt.getItems().add(item);
			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			}
			break;
		}
		accept(SQLToken.RPAREN);

		for (;;) {
			if (lexer.token() == SQLToken.TABLESPACE) {
				lexer.nextToken();
				stmt.setTablespace(this.exprParser.name());
				continue;
			} else if (lexer.token() == SQLToken.PCTFREE) {
				lexer.nextToken();
				stmt.setPtcfree(this.exprParser.expr());
				continue;
			} else if (lexer.token() == SQLToken.INITRANS) {
				lexer.nextToken();
				stmt.setInitrans(this.exprParser.expr());
				continue;
			} else if (lexer.token() == SQLToken.MAXTRANS) {
				lexer.nextToken();
				stmt.setMaxtrans(this.exprParser.expr());
				continue;
			} else if (lexer.token() == SQLToken.COMPUTE) {
				lexer.nextToken();
				acceptIdentifier("STATISTICS");
				stmt.setComputeStatistics(true);
				continue;
			} else if (lexer.token() == SQLToken.ENABLE) {
				lexer.nextToken();
				stmt.setEnable(true);
				continue;
			} else if (lexer.token() == SQLToken.DISABLE) {
				lexer.nextToken();
				stmt.setEnable(false);
				continue;
			} else if (identifierEquals("ONLINE")) {
				lexer.nextToken();
				stmt.setOnline(true);
				continue;
			} else if (identifierEquals("NOPARALLEL")) {
				lexer.nextToken();
				stmt.setNoParallel(true);
				continue;
			} else if (identifierEquals("PARALLEL")) {
				lexer.nextToken();
				stmt.setParallel(this.exprParser.expr());
				continue;
			} else if (lexer.token() == SQLToken.INDEX) {
				lexer.nextToken();
				acceptIdentifier("ONLY");
				acceptIdentifier("TOPLEVEL");
				stmt.setIndexOnlyTopLevel(true);
				continue;
			} else {
				break;
			}
		}
		return stmt;
	}

	public OracleCreateSequenceStatement parseCreateSequence(boolean acceptCreate) {
		if (acceptCreate) {
			accept(SQLToken.CREATE);
		}

		accept(SQLToken.SEQUENCE);

		OracleCreateSequenceStatement stmt = new OracleCreateSequenceStatement();
		stmt.setName(this.exprParser.name());

		for (;;) {
			if (lexer.token() == SQLToken.START) {
				lexer.nextToken();
				accept(SQLToken.WITH);
				stmt.setStartWith(this.exprParser.expr());
				continue;
			} else if (identifierEquals("INCREMENT")) {
				lexer.nextToken();
				accept(SQLToken.BY);
				stmt.setIncrementBy(this.exprParser.expr());
				continue;
			} else if (lexer.token() == SQLToken.CACHE) {
				lexer.nextToken();
				stmt.setCache(Boolean.TRUE);
				continue;
			} else if (lexer.token() == SQLToken.NOCACHE) {
				lexer.nextToken();
				stmt.setCache(Boolean.FALSE);
				continue;
			} else if (identifierEquals("CYCLE")) {
				lexer.nextToken();
				stmt.setCycle(Boolean.TRUE);
				continue;
			} else if (identifierEquals("NOCYCLE")) {
				lexer.nextToken();
				stmt.setCycle(Boolean.FALSE);
				continue;
			} else if (identifierEquals("MINVALUE")) {
				lexer.nextToken();
				stmt.setMinValue(this.exprParser.expr());
				continue;
			} else if (identifierEquals("MAXVALUE")) {
				lexer.nextToken();
				stmt.setMaxValue(this.exprParser.expr());
				continue;
			} else if (identifierEquals("NOMAXVALUE")) {
				lexer.nextToken();
				stmt.setNoMaxValue(true);
				continue;
			} else if (identifierEquals("NOMINVALUE")) {
				lexer.nextToken();
				stmt.setNoMinValue(true);
				continue;
			}
			break;
		}

		return stmt;
	}

	public OracleCreateProcedureStatement parseCreateProcedure() {
		OracleCreateProcedureStatement stmt = new OracleCreateProcedureStatement();
		accept(SQLToken.CREATE);
		if (lexer.token() == SQLToken.OR) {
			lexer.nextToken();
			accept(SQLToken.REPLACE);
			stmt.setOrReplace(true);
		}

		accept(SQLToken.PROCEDURE);

		stmt.setName(this.exprParser.name());

		if (lexer.token() == SQLToken.LPAREN) {
			lexer.nextToken();
			parserParameters(stmt.getParameters());
			accept(SQLToken.RPAREN);
		}

		accept(SQLToken.AS);

		OracleBlockStatement block = this.parseBlock();

		stmt.setBlock(block);

		return stmt;
	}

	public SQLUpdateStatement parseUpdateStatement() {
		return new OracleUpdateParser(this.lexer).parseUpdateStatement();
	}
}
